今天學習使用 MediaPlayer 來播放 mp3 檔案
首先要建立 MediaPlayer 類別
這邊的想法是,建構類別時,把完成後 UI 要做的事情當作 CallBack 傳進來
然後在播放器的完成事件裡觸發它。
其它為相關的方法,後面會一一討論。
class CusMediaPlayer(ctx: Context, val callBack: () -> Unit) {
var mediaPlayer: MediaPlayer = MediaPlayer.create(ctx, R.raw.cc)
init {
mediaPlayer.setOnCompletionListener {
callBack.invoke()
}
}
fun seekToProgress(progress: Int) {...}
fun getDuration(): Int {...}
fun getCurrentPosition(): Int {...}
fun adjustVolume(value: Float) {...}
fun playOrPauseMusic(): String {...}
fun stopMusic(): String {...}
}
首先我們要在 res 資料夾中先新增一個命名為 raw 的資料夾,並把要使用的 mp3 檔案放進去
接著就可以使用 MediaPlayer.create 方法建立 player
var mediaPlayer: MediaPlayer = MediaPlayer.create(ctx, R.raw.cc)
由於播放和暫停鈕是同一個按鈕,我們希望在同一個方法處理它
可以使用 isPlaying 來判斷是否正在播放
最後順便把按鈕應該顯示的文字回傳回去
fun playOrPauseMusic(): String {
if (mediaPlayer.isPlaying) {
mediaPlayer.pause()
return "Play"
} else {
mediaPlayer.start()
return "Pause"
}
}
停止音樂時,首先要把播放的進度使用 seekTo() 方法回歸到 0 (以毫秒計算)
接著呼叫 stop() 方法停止
由於 MediaPlayer 也有自己類似生命週期的定義,所以在 stop() 後會發現其他方法如 start()、pause()...等會失效,此時就要使用 prepare() 方法重新準備好它。
fun stopMusic(): String {
mediaPlayer.seekTo(0)
mediaPlayer.stop()
mediaPlayer.prepare()
return "Play"
}
呼叫 setVolume() 方法來設定,有分左聲道和右聲道,100% 為 1f
fun adjustVolume(value: Float) {
mediaPlayer.setVolume(value / 100f, value / 100f)
}
在 MainActivity 設定 SeekBar 的 setOnSeekBarChangeListener,會在拉動 SeekBar 時觸發。
seekBarVolume.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
cusMediaPlayer.adjustVolume(progress.toFloat())
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
})
一樣是在 onProgressChanged 事件中去呼叫事件,實際是呼叫自訂類別裡的
seekTo() 方法來達成
這邊要注意到我們需要一個 flag 來判斷是不是正在做 "拉動" 這個動作,因為後面我們會使用 Runnable 來讓進度條自動跟著音樂的進度前進,如果沒有做這個判斷的話,這邊就會在沒拉動的時候也不斷觸發。
seekBarProgress.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onStopTrackingTouch(seekBar: SeekBar?) {
isSeeking = false
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
isSeeking = true
}
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
if (isSeeking) {
cusMediaPlayer.seekToProgress(progress)
}
}
})
fun seekToProgress(progress: Int) {
mediaPlayer.seekTo(progress)
}
首先在 MainActivity 中,先把進度條的總長度設定成跟音樂的總毫秒數一樣多
seekBarProgress.max = cusMediaPlayer.getDuration()
fun getDuration(): Int {
return mediaPlayer.duration
}
接著寫一個更新進度條的方法,思路為在 runnable 中呼叫 runnable,達成類似 Timer 的效果
也就是每 500 毫秒更新一次進度條
private fun asyncProgressBar() {
handler = Handler()
runnable = Runnable {
seekBarProgress.progress = cusMediaPlayer.getCurrentPosition()
handler.postDelayed(runnable, 500)
}
runnable.run()
}
fun getCurrentPosition(): Int {
return mediaPlayer.currentPosition
}
後續動畫和錄音部分會放在下篇討論。